# --coding:utf-8--

# Copyright 2021 xpstem.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import struct
from enum import Enum, unique

@unique
class Layer(Enum):
    L0 = 0
    L1 = 1
    L2 = 2
    L3 = 3


@unique
class InputPort(Enum):
    S1 = 0
    S2 = 1
    S3 = 2
    S4 = 3
    A = 16
    B = 17
    C = 18
    D = 19

@unique
class OutputPort(Enum):
    A = 1  # InputPort.A
    B = 2  # InputPort.B
    C = 4  # InputPort.C
    D = 8  # InputPort.D


output_ports = {OutputPort.A: InputPort.A,
                OutputPort.B: InputPort.B,
                OutputPort.C: InputPort.C,
                OutputPort.D: InputPort.D}

@unique
class Brake(Enum):
    FLOAT = 0
    BREAK = 1

class EV3Type:
    __slots__ = ("value")

    def size(self) -> int:
        pass

    def copy(self):
        pass

    def write_to(self, ba: bytearray) -> None:
        pass

    def read_from(self, ba: bytearray, index: int = 0) -> None:
        pass


class Data8(EV3Type):

    SIZE = 1

    def __init__(self, value: int):
        self.value = value

    def size(self) -> int:
        return Data8.SIZE

    def copy(self) -> EV3Type:
        return Data8(self.value)

    def write_to(self, ba: bytearray) -> None:
        ba.append(self.value & 0x000000ff)

    def read_from(self, ba: bytearray, index: int = 0) -> None:
        while index > 0:
            ba.pop(0)
            index = index-1
        self.value = ba.pop(index)


class Data16(EV3Type):

    SIZE = 2

    def __init__(self, value: int):
        self.value = value

    def size(self) -> int:
        return Data16.SIZE

    def copy(self) -> EV3Type:
        return Data16(self.value)

    def write_to(self, ba: bytearray) -> None:
        ba.append(self.value & 0x00ff)
        ba.append(self.value >> 8)

    def read_from(self, ba: bytearray, index: int = 0) -> None:
        while index > 0:
            ba.pop(0)
            index = index-1
        b0 = ba.pop(index)
        b1 = ba.pop(index)
        self.value = b0 + (b1 << 8)


class Data32(EV3Type):

    SIZE = 4

    def __init__(self, value: int):
        self.value = value

    def size(self) -> int:
        return Data32.SIZE

    def copy(self) -> EV3Type:
        return Data32(self.value)

    def write_to(self, ba: bytearray) -> None:
        ba.append(self.value & 0x000000ff)
        ba.append((self.value >> 8) & 0x000000ff)
        ba.append((self.value >> 16) & 0x000000ff)
        ba.append((self.value >> 24) & 0x000000ff)

    def read_from(self, ba: bytearray, index: int = 0) -> None:
        while index > 0:
            ba.pop(0)
            index = index-1
        b0 = ba.pop(index)
        b1 = ba.pop(index)
        b2 = ba.pop(index)
        b3 = ba.pop(index)
        self.value = b0 + (b1 << 8) + (b2 << 16) + (b3 << 24)


class DataF(EV3Type):

    SIZE = 4

    def __init__(self, value: float):
        self.value = value

    def size(self) -> int:
        return DataF.SIZE

    def copy(self) -> EV3Type:
        return DataF(self.value)

    def write_to(self, ba: bytearray) -> None:
        bytes_ = struct.pack("<f", self.value)
        ba.extend(bytes_)

    def read_from(self, ba: bytearray, index: int = 0) -> None:
        while index > 0:
            ba.pop(0)
            index = index-1
        bytes_ = bytearray()
        bytes_.append(ba.pop(index))
        bytes_.append(ba.pop(index))
        bytes_.append(ba.pop(index))
        bytes_.append(ba.pop(index))
        return struct.unpack("<f", bytes_)


class DataS(EV3Type):
    __slots__ = ("_length")

    def __init__(self, obj):
        if isinstance(obj, int):
            self._length = obj
        elif isinstance(obj, str):
            self.value = obj
            self._length = len(obj)

    def size(self) -> int:
        return self._length

    def write_to(self, ba: bytearray) -> None:
        ba.extend(str.encode('utf-8'))
        ba.append(0x00)

    def read_from(self, ba: bytearray, index: int = 0) -> None:
        while index > 0:
            ba.pop(0)
            index = index-1
        buf = bytearray()
        while len(ba) > 0:
            b = ba.pop(index)
            if b == 0x00:
                break
            buf.append(b)
        self.value = buf.decode('utf-8')

